package com.bby.disk.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.bby.common.config.GlobalSettings;
import com.bby.common.exception.BBYException;
import com.bby.common.model.FileReport;
import com.bby.common.model.Permission;
import com.bby.common.model.UserSettings;
import com.bby.common.utils.BBYUtils;
import com.bby.common.utils.StatusCode;
import com.bby.disk.bean.vo.SearchFileVO;
import com.bby.disk.bean.vo.UserRootsVO;
import com.bby.disk.bean.req.EditPermissionReq;
import com.bby.disk.manager.auth.AuthorityManager;
import com.bby.disk.bean.vo.VirtualFileList;
import com.bby.disk.bean.req.FileActionReq;
import com.bby.disk.bean.vo.FileActionResult;
import com.bby.disk.exception.BBYExceptionHandler;
import com.bby.disk.manager.virtual.*;
import com.bby.disk.manager.mfile.MFileLog;
import com.bby.disk.service.IFileService;
import com.bby.disk.service.UserService;
import com.bby.disk.util.Judges;
import com.mongodb.client.MongoCursor;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;


@Service
public class FileService implements IFileService {
    UserService userService;
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private MongoTemplate mongoTemplate;

    public FileService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public VirtualFileList getFileList(String userId, VirtualPath path) throws BBYException {
        return getFileList(userId, VirtualManager.findFileByPath(path).getId());
    }

    @Override
    public VirtualFileList getFileList(String userId, String fileId) throws BBYException {
        return AuthorityManager.list(userId, fileId);
    }

    @Override
    public FileActionResult renameFile(String userId, FileActionReq action) throws BBYException {
        if (ObjectUtil.isEmpty(action.getFileList())) {
            throw BBYExceptionHandler.INPUT_ERROR_EX;
        }
        boolean allOk = true;
        boolean allError = true;
        Map<String, String> res = new HashMap<>();
        for (Map<String, String> item : action.getFileList()) {
            String fileId = item.get("id");
            String newName = item.get("newName");
            if (ObjectUtil.isEmpty(fileId) || ObjectUtil.isEmpty(newName)) {
                res.put(fileId, "参数错误！");
                allOk = false;
                continue;
            }
            try {
                AuthorityManager.rename(userId, fileId);
                VirtualManager.rename(fileId, newName);
                res.put(fileId, "success");
                allError = false;
            } catch (BBYException e) {
                res.put(fileId, e.getMessage());
                allOk = false;
            }
        }
        MFileLog.addLog(userId, MFileLog.ACTION_FILE_RENAME, res);//写日志
        return new FileActionResult(allOk, allError, res);
    }

    @Override
    public FileActionResult copyAndMove(String userId, FileActionReq action) throws BBYException {
        if (ObjectUtil.isEmpty(action.getFileList())) {
            throw BBYExceptionHandler.INPUT_ERROR_EX;
        }
        boolean allOk = true;
        boolean allError = true;
        boolean copy = "copy".equalsIgnoreCase(action.getOpera());
        Map<String, String> res = new HashMap<>();
        UserSettings userSettings = VirtualManager.getUserSettings(userId);
        // 新文件权限
        Permission permission = null;
        boolean idExtends = false;
        if ("custom".equals(userSettings.getDefaultPmType())){
            permission = userSettings.getDefaultPermission();
        }else  if ("extends".equals(userSettings.getDefaultPmType())){
            idExtends = true;
        }
        for (Map<String, String> item : action.getFileList()) {
            String from = item.get("from");
            String to = item.get("to");
            if (ObjectUtil.isEmpty(from) || ObjectUtil.isEmpty(to)) {
                res.put(from, "参数错误！");
                allOk = false;
                continue;
            }
            try {
                VirtualObject toFile = AuthorityManager.copyAndMove(userId, from, to, copy);
                if (copy) {
                    if (idExtends){
                        permission = VirtualManager.findFile(from).getPermission();
                    }
                    VirtualManager.copy(toFile.getOwner(), from, to,permission);
                } else {
                    VirtualManager.move(from, to);
                }
                res.put(from, "success");
                allError = false;
            } catch (BBYException e) {
                res.put(from, e.getMessage());
                allOk = false;
            }
        }
        MFileLog.addLog(userId, copy ? MFileLog.ACTION_FILE_COPY : MFileLog.ACTION_FILE_MOVE, res);//写日志
        return new FileActionResult(allOk, allError, res);
    }

    @Override
    public FileActionResult deleteFile(String userId, FileActionReq action) throws BBYException {
        if (ObjectUtil.isEmpty(action.getFileList())) {
            throw BBYExceptionHandler.INPUT_ERROR_EX;
        }
        boolean allOk = true;
        boolean allError = true;
        Map<String, String> res = new HashMap<>();
        for (Map<String, String> item : action.getFileList()) {
            String fileId = item.get("id");
            if (ObjectUtil.isEmpty(fileId)) {
                res.put(fileId, "参数错误！");
                allOk = false;
                continue;
            }
            try {
                AuthorityManager.delete(userId, fileId);
                VirtualManager.delete(fileId);
                res.put(fileId, "success");
                allError = false;
            } catch (BBYException e) {
                res.put(fileId, e.getMessage());
                allOk = false;
            }
        }
        MFileLog.addLog(userId, MFileLog.ACTION_FILE_DELETE, res);//写日志
        return new FileActionResult(allOk, allError, res);
    }

    @Override
    public VirtualDirectory mkdir(String userId, String parent, String dirname) throws BBYException {
        BBYUtils.checkEmpty(parent,dirname);
        if (!Judges.judgeDirname(dirname)) {
            throw new BBYException(StatusCode.NAME_ERROR, "文件名不符合规范!");
        }

        VirtualDirectory parentDir = AuthorityManager.create(userId, parent);
        // 文件信息设置
        Date date = new Date();
        VirtualDirectory directory = new VirtualDirectory();
        Permission permission = VirtualManager.getDefaultPermission(parentDir.getOwner(), parentDir);
        directory.setId(null).setMaker(ObjectUtil.isEmpty(userId) ? "ann" : userId)
                .setName(dirname).setParent(parent)
                .setOwner(parentDir.getOwner()).setCreateTime(date)
                .setLastModified(date).setFile(false)
                .setPermission(permission);
        VirtualManager.create(directory);
        MFileLog.addLog(userId, MFileLog.ACTION_FILE_MKDIR, directory);//写日志
        return directory;
    }

    @Override
    public void editPermission(String userId, EditPermissionReq req) throws BBYException {
        List<VirtualObject> files = VirtualManager.findFiles(req.getIds());
        if (files.size() != req.getIds().size()) {
            throw BBYExceptionHandler.FILE_NOT_FOUNT;
        }
        for (VirtualObject file : files) {
            if (!file.getOwner().equals(userId)) {
                throw BBYExceptionHandler.NON_AUTHORITY_EX;
            }
        }
        for (VirtualObject file : files) {
            VirtualManager.editPermission(file, req.getPermission());
        }
    }

    @Override
    public SearchFileVO search(String userId, String target, String keyword, String type, int page, int limit) throws BBYException {
        BBYUtils.checkEmpty(keyword);
        int skip = (page - 1) * limit;
        if (ObjectUtil.isEmpty(target)) {
//            throw new BBYException("暂时不支持全局搜索！");
            logger.debug("全局搜索");
            MongoCursor<Document> cursor = VirtualManager.searchGlobal(keyword,type);
            return dealSearchDocuments(userId,null,cursor,skip,limit);
        } else {
            VirtualDirectory directory = VirtualManager.findDirectory(target);
            if (ObjectUtil.isEmpty(directory.getParent())) {
                logger.debug("在网盘中搜索：keyword = {}", keyword);
                MongoCursor<Document> cursor = VirtualManager.searchByOwner(directory.getId(), keyword,type);
                return dealSearchDocuments(userId,directory,cursor,skip,limit);
            } else {// 在文件夹中搜索，已经进行权限筛选
                logger.debug("在文件夹中搜索：keyword = {}", keyword);
                int flag=0;
                if ("file".equals(type)){
                    flag=1;
                }else if ("dir".equals(type)){
                    flag=2;
                }
                SearchFileVO res = new SearchFileVO(limit);
                res.setArea(directory);
                searchByDir(userId, res, directory, keyword,flag, skip);
                return res;
            }
        }
    }

    @Override
    public List<VirtualFile> classify(String userId, String category) throws BBYException {
        BBYUtils.checkEmpty(category);
        if (!category.matches("image|text|video|audio|zip")){
            throw new BBYException(StatusCode.INPUT_ERROR,"参数不符合规范");
        }
        return VirtualManager.classify(userId,category);
    }

    @Override
    public UserRootsVO getUserRoots(String userId, int page, int limit) {
        int total = 0, skip_cnt = 0;
        int skip = (page - 1) * limit;
        boolean isSkip = page > 1;// 跳过，不统计
        List<String> list = new ArrayList<>();
        MongoCursor<Document> cursor = VirtualManager.getRootsIterator();
        Document document;
        while (cursor.hasNext()) {
            document = cursor.next();
            VirtualObject file = JSON.parseObject(document.toJson(), VirtualObject.class);
            if (AuthorityManager.canView(userId, file)) {
                if (isSkip) {
                    if (skip_cnt < skip) {
                        skip_cnt++;
                        continue;
                    }
                    if (total >= limit) {
                        break;
                    }
                }
                total++;
                list.add(document.getObjectId("_id").toHexString());
                if (total >= GlobalSettings.MAX_SEARCH_LIMIT) {
                    break;
                }
            }
        }
        if (skip_cnt == 0 && total > limit) {
            list = list.subList(skip, skip + limit);
        }
        logger.debug("{}获取用户共享列表,跳过:{},总数={}", userId, skip, total);
        return new UserRootsVO(total, userService.getUserPubInfos(list));
    }

    @Override
    public VirtualObject getFileInfo(String userId, String fileId) throws BBYException {
        VirtualObject file = VirtualManager.findFile(fileId);
        if (AuthorityManager.canView(userId, file)) {
            return file;
        }
        throw BBYExceptionHandler.NON_AUTHORITY_EX;
    }

    @Override
    public List<VirtualObject> getFileInfos(String userId, List<String> fileIds) {
        List<VirtualObject> list = VirtualManager.findFiles(fileIds);
        List<VirtualObject> result = new ArrayList<>();
        for (VirtualObject file : list) {
            if (AuthorityManager.canView(userId,file)){
                result.add(file);
            }
        }
        return result;
    }

    @Override
    public String getFilePath(String userId, String fileId) throws BBYException {
        VirtualObject file = VirtualManager.findFile(fileId);
        if (AuthorityManager.canView(userId, file)) {
            return VirtualManager.findVirtualPath(fileId);
        }
        throw BBYExceptionHandler.NON_AUTHORITY_EX;
    }

    @Override
    public void reportFile(FileReport report) throws VirtualObjNotFoundException {
        report.setId(null);
        VirtualObject file = VirtualManager.findFile(report.getFileId());
        report.setOwner(file.getOwner());
        report.setMaker(file.getMaker());
        report.setCreateTime(new Date());
        report.setDeal(false);
        mongoTemplate.save(report);
    }

    private SearchFileVO dealSearchDocuments(String userId, VirtualDirectory directory, MongoCursor<Document> cursor, int skip, int limit) throws BBYException {
        Document document;
        List<VirtualObject> list = new ArrayList<>();
        int skip_cnt = 0;
        int total = 0;
        Map<String,Boolean> closedUserIdMap = new HashMap<>();
        while (cursor.hasNext()) {
            document = cursor.next();
            Boolean isFile = document.getBoolean("file");
            VirtualObject file;
            if (isFile==Boolean.TRUE) {
                file = BBYUtils.toBean(document.toJson(),VirtualFile.class);
            } else {
                file = BBYUtils.toBean(document.toJson(), VirtualObject.class);
            }
            if (directory==null){
                Boolean flag = closedUserIdMap.get(file.getOwner());
                if (flag==null){
                    flag = VirtualManager.isBlockSearch(file.getOwner());
                    closedUserIdMap.put(file.getOwner(),flag);
                }
                if (flag){
                    continue;// 此用户不许搜索
                }
            }

            if (AuthorityManager.canView(userId, file)) {
                if (skip_cnt < skip) {
                    skip_cnt++;
                    continue;
                }
                list.add(file);
                total++;
                if (total == limit) {
                    break;
                }
            }
        }
        return new SearchFileVO(list, directory, total, !cursor.hasNext());
    }


    /**
     * 在文件夹中递归搜索,广度优先，会判断权限
     * 从外到内递归比较contains
     */
    private void searchByDir(String userId, SearchFileVO result, VirtualDirectory directory,
                                            String keyword, int flag, int skip) {
        List<VirtualObject> list = result.getList();
        List<VirtualDirectory> dirs = new ArrayList<>();
        int cnt = 0;
        for (VirtualObject file : VirtualManager.findFiles(directory.getFiles())) {
            if (file.isFile()) {
                if (flag==2){
                    continue;// 只搜索文件夹
                }
            }else {// 是文件夹
                dirs.add((VirtualDirectory) file);
                if (flag == 1){
                    continue;// 只搜索文件
                }
            }
            if (file.getName().toLowerCase().contains(keyword.toLowerCase())) {
                if (!AuthorityManager.canView(userId, file)) {
                    continue;
                }
                if (cnt < skip) {
                    cnt++;
                    continue;
                }
                list.add(file);
                if (list.size() >= result.getTotal()) {
                    result.setEnd(false);// 中间截取分页，数据未结束
                    return;
                }
            }
        }
        for (VirtualDirectory dir : dirs) {
            searchByDir(userId, result, dir,keyword,flag, skip - cnt);
            if (!result.isEnd()) {// res默认为true，为false时是中间截取分页，因此停止
                return;
            }
        }
    }
}
